package com.pkk.penguin.frame.know.java.Javacoreknowledge.thread.threaduse;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.junit.Test;

/**
 * @description: 线程等特性的使用
 * @author: peikunkun
 * @create: 2020-01-10 17:01
 **/
public class ThreadCountDownMoreSimple {


  /**
   * @Description: CountDownLatch计数器的简单使用
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/1/10 0010 下午 5:40
   */
  @Test
  public void Administrator_12_20200110170153() throws InterruptedException {
    System.out.println("欢迎使用单元测试方法【Administrator_12()_20200110170153】");
    System.out.println("此方法测试描述：【CountDownLatch】");
    System.out.println("CountDownLatch为程序计数器,调用countDown让记数减一,await使线程进入阻塞状态,知道记数为0");

    //核心线程数-始终保留的线程数
    int corePoolSize = 10;
    //最大线程数
    int maximumPoolSize = 20;
    //空闲线程存活时间
    long keepAliveTime = 1L;
    //空闲线程存活时间单位
    TimeUnit unit = TimeUnit.SECONDS;
    //任务队列
    BlockingQueue<Runnable> workQueue = new LinkedTransferQueue();

    //创建线程池
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
        workQueue);

    //任务数
    int taskNum = 30;

    //程序计数器
    CountDownLatch countDownLatch = new CountDownLatch(taskNum);

    for (int i = 0; i < taskNum; i++) {
      threadPoolExecutor.execute(new Runnable() {
        @Override
        public void run() {
          System.out.println(Thread.currentThread().getName() + "is Running");
          //线程完成,记数减一
          countDownLatch.countDown();
        }
      });
    }

    //是线程进入则堵塞状态,直至【程序计数器】为0
    System.out.println("主线程执行ing");
    countDownLatch.await();
    System.out.println("主线程执行结束");
  }


  /**
   * @Description: 回环阑珊的简单运行
   * @Param:
   * @return: void
   * @Author: peikunkun
   * @Date: 2020/1/10 0010 下午 5:39
   */
  @Test
  public void Administrator_64_20200110171202() throws ExecutionException, InterruptedException {
    System.out.println("欢迎使用单元测试方法【Administrator_64()_20200110171202】");
    System.out.println("此方法测试描述：【回环栅栏-等待至 barrier 状态再全部同时执行】");
    System.out.println("通过CyclicBarrier的await方法让线程进入barrier状态");
    System.out.println("await(long timeout, TimeUnit unit)：让这些线程等待至一定的时间，如果还有\n"
        + "线程没有到达 barrier 状态就直接让到达 barrier 的线程执行后续任务\n"
        + " CyclicBarrier 是可以重用的\n");
    System.out.println("当线程数达到barrier指定的数量,就同时释放阻塞,线程是可以复用的");
    System.out.println("_______________________________________________________________________");

    //任务数
    int taskNum = 4;

    CountDownLatch countDownLatch = new CountDownLatch(taskNum);

    //回环-阑珊
    CyclicBarrier cyclicBarrier = new CyclicBarrier(taskNum);
    for (int i = 0; i < taskNum; i++) {
      new Writer(cyclicBarrier, countDownLatch).start();
    }
    countDownLatch.await();
  }


  @Test
  public void Administrator_105_20200110174447() throws InterruptedException {
    System.out.println("欢迎使用单元测试方法【Administrator_105()_20200110174447】");
    System.out.println("此方法测试描述：【\n"
        + "Semaphore 翻译成字面意思为 信号量，Semaphore 可以控制同时访问的线程个数，通过\n"
        + "acquire() 获取一个许可，如果没有就等待，而 release() 释放一个许可。\n"
        + "Semaphore 类中比较重要的几个方法：\n"
        + "1.  public void acquire(): 用来获取一个许可，若无许可能够获得，则会一直等待，直到获得许可。\n"
        + "2.  public void acquire(int permits):获取 permits 个许可\n"
        + "3.  public void release() { } :释放许可。注意，在释放许可之前，必须先获获得许可。\n"
        + "4.  public void release(int permits) { }:释放 permits 个许可\n"
        + "上面 4 个方法都会被阻塞，如果想立即得到执行结果，可以使用下面几个方法\n"
        + "1.  public boolean tryAcquire():尝试获取一个许可，若获取成功，则立即返回 true，若获取失败，则立即返回 false\n"
        + "2.  public boolean tryAcquire(long timeout, TimeUnit unit):尝试获取一个许可，若在指定的时间内获取成功，则立即返回 true，否则则立即返回 false\n"
        + "3.  public boolean tryAcquire(int permits):尝试获取 permits 个许可，若获取成功，则立即返回 true，若获取失败，则立即返回 false\n"
        + "4.  public boolean tryAcquire(int permits, long timeout, TimeUnit unit): 尝试获取 permits个许可，若在指定的时间内获取成功，则立即返回 true，否则则立即返回 false\n"
        + "5.  还可以通过 availablePermits()方法得到可用的许可数目】");

    int num = 3;

    Semaphore semaphore = new Semaphore(num);
    System.out.println("当前容量中的许可数量:" + semaphore.availablePermits());

    CountDownLatch countDownLatch = new CountDownLatch(num);

    for (int i = 0; i < 10; i++) {
      new Thread(new Runnable() {
        @Override
        public void run() {
          //堵塞获取一个许可
          //final boolean b = semaphore.acquire();
          //非堵塞获取一个许可
          final boolean b = semaphore.tryAcquire();
          final String name = Thread.currentThread().getName();
          System.out.println(name + "-是否获取到许可?" + b);
          try {
            if (b) {
              //线程进入阻塞状态,并不释放锁
              Thread.sleep(1000L);
            } else {
              Thread.sleep(1000L);
              System.out.println(name + "-是否获取到许可?" + b);
            }
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
          countDownLatch.countDown();
        }
      }).start();
    }
    countDownLatch.await();
  }

  /**
   * @Description:CyclicBarrier回环阑珊的简单支持
   * @Param: null
   * @return:
   * @Author: peikunkun
   * @Date: 2020/1/10 0010 下午 5:39
   */
  static class Writer extends Thread {

    private CyclicBarrier cyclicBarrier;
    //这个主要是等待子线程的执行完防止主线程结束的早
    private CountDownLatch countDownLatch;

    public Writer(CyclicBarrier cyclicBarrier, CountDownLatch countDownLatch) {
      this.cyclicBarrier = cyclicBarrier;
      this.countDownLatch = countDownLatch;
    }

    @Override
    public void run() {
      try {
        Thread.sleep(5000); //以睡眠来模拟线程需要预定写入数据操作
        System.out.println("线程" + Thread.currentThread().getName() + "写入数据完毕，等待其他线程写入完毕");
        cyclicBarrier.await();
      } catch (InterruptedException | BrokenBarrierException e) {
        e.printStackTrace();
      }
      System.out.println(Thread.currentThread().getName() + "所有线程写入完毕，继续处理其他任务，比如数据操作");
      countDownLatch.countDown();
    }
  }
}
